#include <console.h>
#include <device.h>
#include <list.h>
#include <util.h>
#include <errno.h>

static LIST_HEAD(consoles);

static struct console *input_console = NULL;

int console_register(struct console *con)
{
	if (IS_ERR(con) || (con == NULL))
		return -EINVAL;

	console_enable(con);
	list_add_tail(&con->n_console_list, &consoles);

	if (input_console == NULL) 
		console_set_input(con);

	return 0;
}

int console_unregister(struct console *con)
{
	if (IS_ERR(con) || (con == NULL))
		return -EINVAL;

	console_disable(con);
	list_del_init(&con->n_console_list);

	if (input_console == con) {
		input_console = NULL;
		if (!list_empty(&consoles)) 
			console_set_input(container_of(consoles.next, struct console, n_console_list));
	}
	return 0;
}

int console_enable(struct console *con)
{
	if (IS_ERR(con) || (con == NULL))
		return -EINVAL;

	console_flags_set_enabled(con->flags);
	return 0;
}

int console_disable(struct console *con)
{
	if (IS_ERR(con) || (con == NULL))
		return -EINVAL;

	console_flags_set_disabled(con->flags);
	return 0;
}

int console_set_input(struct console *con)
{
	if (IS_ERR(con) || (con == NULL))
		return -EINVAL;

	if (input_console) 
		console_flags_clr_input(input_console->flags);

	console_flags_set_input(con->flags);
	input_console = con;
	console_enable(con);
	return 0;
}

int console_open(void)
{
	struct console *con;
	struct list_head *p;
	int ret = 0;
	
	list_for_each(p, &consoles) {
		con = container_of(p, struct console, n_console_list);
		if (con == input_console) {
			ret = device_open(console_to_device(con), DEV_OFLAGS_RDWR | DEV_OFLAGS_NONBLOCK);
		} else {
			ret = device_open(console_to_device(con), DEV_OFLAGS_WRITE | DEV_OFLAGS_NONBLOCK);
		}
		if (IS_ERR(ret))
			break;
	}

	return ret;
}

void console_close(void)
{
	struct console *con;
	struct list_head *p;
	
	list_for_each(p, &consoles) {
		con = container_of(p, struct console, n_console_list);
		device_close(console_to_device(con));
	}
}

void console_flush(void)
{
	struct console *con;
	struct list_head *p;
	
	list_for_each(p, &consoles) {
		con = container_of(p, struct console, n_console_list);
		if (console_flags_is_enabled(con->flags)) {
			device_flush(console_to_device(con));
		}
	}
}

int console_write(const char *buf, unsigned int count)
{
	struct console *con;
	struct list_head *p;
	unsigned int n;
	int ret;

	if (IS_ERR(buf) || !buf || (count == 0))
		return -EINVAL;

	list_for_each(p, &consoles) {
		con = container_of(p, struct console, n_console_list);
		if (console_flags_is_enabled(con->flags)) {
			n = 0;
			do {
				ret = device_write(console_to_device(con), buf + n, count - n);
				if (IS_ERR(ret))
					return ret;
				n += ret;
			} while (n < count);
		}
	}

	return 0;
}

int console_read(char *buf, unsigned int count)
{
	struct console *con = input_console;
	unsigned int n;
	int ret;

	if (IS_ERR(buf) || !buf || (count == 0) || !con)
		return -EINVAL;

	if (console_flags_is_enabled(con->flags)) {
		n = 0;
		do {
			ret = device_read(console_to_device(con), buf + n, count - n);
			if (IS_ERR(ret))
				return ret;
			n += ret;
		} while (n < count);
	}

	return 0;
}

